package com.docmgmt.mvp.ragflow.dto;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * AI 问答流式返回事件 DTO
 * 
 * 用于 Server-Sent Events (SSE) 流式返回 AI 回答。
 * 
 * 采用混合模式设计，支持多种事件类型：
 * - start：会话开始
 * - token：逐字/逐句返回 AI 回答
 * - reference：返回引用的文档片段
 * - done：回答完成，返回完整数据
 * 
 * 事件流示例：
 * <pre>
 * data: {"type":"start","session_id":"session_xxx"}
 * 
 * data: {"type":"token","content":"RAGFlow"}
 * 
 * data: {"type":"token","content":" 是"}
 * 
 * data: {"type":"reference","reference":{"chunk_id":"...","similarity":0.85}}
 * 
 * data: {"type":"done","answer":"完整答案","references":[...]}
 * </pre>
 * 
 * 前端处理示例：
 * <pre>
 * const eventSource = new EventSource('/api/v1/ai/chat/xxx/completions');
 * eventSource.onmessage = (event) => {
 *   const data = JSON.parse(event.data);
 *   switch (data.type) {
 *     case 'start':
 *       console.log('会话开始：', data.session_id);
 *       break;
 *     case 'token':
 *       appendToAnswer(data.content);  // 逐字显示
 *       break;
 *     case 'reference':
 *       addReference(data.reference);  // 添加引用
 *       break;
 *     case 'done':
 *       showCompleteAnswer(data.answer, data.references);
 *       eventSource.close();
 *       break;
 *   }
 * };
 * </pre>
 * 
 * @author Dora
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)  // 只序列化非 null 字段
public class CompletionStreamEvent {

    /**
     * 事件类型
     * 
     * 可选值：
     * - start：会话开始事件
     * - token：AI 回答的一个 token（字/词）
     * - reference：引用的文档片段
     * - done：回答完成事件
     * 
     * 必填字段，每个事件都必须指定类型。
     */
    private String type;

    /**
     * Token 内容（仅 type=token 时使用）
     * 
     * 表示 AI 生成的一个字、词或短语。
     * 
     * 特点：
     * - 长度不固定，可能是单个字符或多个字符
     * - 按生成顺序发送
     * - 前端需要累积拼接成完整答案
     * 
     * 示例：
     * - "RAGFlow"
     * - " 是"
     * - "一个"
     * - "开源的"
     * 
     * 注意：
     * - 可能包含空格、标点符号
     * - 需要前端处理 Markdown 格式
     */
    private String content;

    /**
     * 会话 ID（type=start 或 done 时使用）
     * 
     * 标识当前对话所属的会话。
     * 
     * 使用场景：
     * - type=start：在第一个事件中返回会话 ID
     * - type=done：在最后一个事件中再次返回会话 ID
     * 
     * 用途：
     * - 前端保存此 ID，用于后续的多轮对话
     * - 关联对话历史
     */
    @JsonProperty("session_id")
    private String sessionId;

    /**
     * 单个引用数据（仅 type=reference 时使用）
     * 
     * 表示 AI 回答时引用的一个文档片段。
     * 
     * 发送时机：
     * - 在生成答案的过程中，实时发送引用信息
     * - 通常在相关 token 发送后立即发送
     * 
     * 用途：
     * - 前端可以实时显示引用来源
     * - 提前加载引用内容，提升用户体验
     * 
     * 注意：
     * - 同一个 chunk 可能被多次引用，会发送多个 reference 事件
     * - 最终的完整引用列表在 type=done 事件中返回
     */
    private CompletionResponse.ChunkReference reference;

    /**
     * 完整答案（仅 type=done 时使用）
     * 
     * AI 生成的完整回答，包含所有 token 拼接后的结果。
     * 
     * 用途：
     * - 前端可以用此字段验证累积的 token 是否正确
     * - 如果前端累积失败，可以直接使用此字段
     * - 保存到数据库时使用此字段
     * 
     * 注意：
     * - 此字段应该与前端累积的 token 完全一致
     * - 如果不一致，说明前端处理有误或网络丢包
     */
    private String answer;

    /**
     * 所有引用列表（仅 type=done 时使用）
     * 
     * 包含 AI 回答时参考的所有文档片段，去重后的完整列表。
     * 
     * 特点：
     * - 按相似度从高到低排序
     * - 已去重（同一个 chunk 只出现一次）
     * - 包含完整的引用信息（positions 字段已填充）
     * 
     * 用途：
     * - 前端展示完整的引用列表
     * - 保存到数据库
     * - 生成引用报告
     * 
     * 注意：
     * - 此列表应该包含所有 type=reference 事件中的 chunk
     * - 如果前端累积的引用列表与此不一致，以此字段为准
     */
    private List<CompletionResponse.ChunkReference> references;

    // ==================== 静态工厂方法 ====================

    /**
     * 创建会话开始事件
     * 
     * @param sessionId 会话 ID
     * @return 会话开始事件
     */
    public static CompletionStreamEvent start(String sessionId) {
        return CompletionStreamEvent.builder()
                .type("start")
                .sessionId(sessionId)
                .build();
    }

    /**
     * 创建 token 事件
     * 
     * @param content token 内容
     * @return token 事件
     */
    public static CompletionStreamEvent token(String content) {
        return CompletionStreamEvent.builder()
                .type("token")
                .content(content)
                .build();
    }

    /**
     * 创建引用事件
     * 
     * @param reference 引用数据
     * @return 引用事件
     */
    public static CompletionStreamEvent reference(CompletionResponse.ChunkReference reference) {
        return CompletionStreamEvent.builder()
                .type("reference")
                .reference(reference)
                .build();
    }

    /**
     * 创建完成事件
     * 
     * @param answer     完整答案
     * @param references 所有引用列表
     * @param sessionId  会话 ID
     * @return 完成事件
     */
    public static CompletionStreamEvent done(String answer, List<CompletionResponse.ChunkReference> references, String sessionId) {
        return CompletionStreamEvent.builder()
                .type("done")
                .answer(answer)
                .references(references)
                .sessionId(sessionId)
                .build();
    }

    // ==================== 辅助方法 ====================

    /**
     * 判断是否为开始事件
     * 
     * @return 是否为开始事件
     */
    public boolean isStart() {
        return "start".equals(type);
    }

    /**
     * 判断是否为 token 事件
     * 
     * @return 是否为 token 事件
     */
    public boolean isToken() {
        return "token".equals(type);
    }

    /**
     * 判断是否为引用事件
     * 
     * @return 是否为引用事件
     */
    public boolean isReference() {
        return "reference".equals(type);
    }

    /**
     * 判断是否为完成事件
     * 
     * @return 是否为完成事件
     */
    public boolean isDone() {
        return "done".equals(type);
    }
}

